#include "sdGraphics.h"

sdUniverse* sdGraphics::universe_;

#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif


void sdGraphics::drawCircle(cpFloat x, cpFloat y, cpFloat r, cpFloat a) {
    const int segs = 15;
    const cpFloat coef = 2.0*M_PI/(cpFloat)segs;

    glBegin(GL_LINE_STRIP); {
        for(int n = 0; n <= segs; n++){
            cpFloat rads = n*coef;
            glVertex2f(r*cos(rads + a) + x, r*sin(rads + a) + y);
        }
    } 
    glEnd();
}

void sdGraphics::drawCircleShape(cpShape *shape, sdObject* object) {
	sdTimed* chunk;
	sdBad* bad;
	cpFloat percent;
	cpFloat r;
	cpFloat g;
	cpFloat b;
	
	switch(shape->collision_type) {
	case 3: //Bad
	    bad = (sdBad*) object;
		percent = (bad->getHp() / bad->getMaxHp());
		glColor4f(1.0, 1.0 - percent, 0.0, 1.0);
		break;

	case 4: //Ship chunk
    	chunk = (sdTimed*) object;
		percent = (chunk->getTtl() / chunk->maxTtl());
		r = rand();
		r /= RAND_MAX;
		g = rand();
		g /= RAND_MAX;
		b = rand();
		b /= RAND_MAX;
		glColor4f(r, g, b, percent); 
		break;

	case 5: //BulletChunk
	    chunk = (sdTimed*) object;
		if(chunk->dying()) {
			cpFloat percent = (chunk->getTtl() / chunk->maxTtl());
			glColor4f(1.0, 0.5, 0.0, percent);
		} else {
			glColor4f(1.0, 0.5, 0.0, 1.0);
		}
	    break;

	case 6: //Bad Chunk
		chunk = (sdTimed*) object;
		if(chunk->dying()) {
			cpFloat percent = (chunk->getTtl() / chunk->maxTtl());
			glColor4f(1.0, 1.0, 0.0, percent);
		} else {
			glColor4f(1.0, 1.0, 0.0, 1.0);
		}
	    break;

	case 7: //Bad Chunk Chunks
		chunk = (sdTimed*) object;
		if(chunk->dying()) {
			cpFloat percent = (chunk->getTtl() / chunk->maxTtl());
			glColor4f(1.0, 1.0, 0.0, percent);
		} else {
			glColor4f(1.0, 1.0, 0.0, 1.0);
		}
	    break;

	}
	
	cpBody *body = shape->body;
    cpCircleShape *circle = (cpCircleShape *)shape;
    cpVect c = cpvadd(body->p, cpvrotate(circle->c, body->rot));
    drawCircle(c.x, c.y, circle->r, body->a);
}

void sdGraphics::drawSegmentShape(cpShape *shape, sdObject* object) {
    cpBody *body = shape->body;
    cpSegmentShape *seg = (cpSegmentShape *)shape;
    cpVect a = cpvadd(body->p, cpvrotate(seg->a, body->rot));
    cpVect b = cpvadd(body->p, cpvrotate(seg->b, body->rot));

    glBegin(GL_LINES); {
        glVertex2f(a.x, a.y);
        glVertex2f(b.x, b.y);
    } 

    glEnd();
}

void sdGraphics::drawPolyShape(cpShape *shape, sdObject* object) {
    sdShip* ship;
	sdTimed* chunk;
	cpFloat percent;
	
	switch(shape->collision_type) {
	case 0: //Walls
		glColor4f(0.0, 1.0, 0.0, 1.0);
	case 1: //Ship
		ship = (sdShip*) object;
		percent = (ship->getHp() / ship->getMaxHp());
		glColor4f(1 - percent, percent, 0.0, 1.0);
		break;

	case 2: //Bullet
		chunk = (sdTimed*) object;
		if(chunk->dying()) {
			cpFloat percent = (chunk->getTtl() / chunk->maxTtl());
			glColor4f(1.0, 0.5, 0.0, percent);
		} else {
			glColor4f(1.0, 0.5, 0.0, 1.0);
		}
		break;
	}
	
	cpBody *body = shape->body;
    cpPolyShape *poly = (cpPolyShape *)shape;

    int num = poly->numVerts;
    cpVect *verts = poly->verts;

    glBegin(GL_LINE_LOOP);

    for(int i=0; i<num; i++) {
        cpVect v = cpvadd(body->p, cpvrotate(verts[i], body->rot));
        glVertex2f(v.x, v.y);
    } 

    glEnd();
}

void sdGraphics::drawObject(void *ptr, void *unused) {
    cpShape *shape = (cpShape *)ptr;

	std::map<cpShape*, sdObject*>* objects = (std::map<cpShape*, sdObject*>*) unused;

	sdObject* object = (*objects)[shape];

    switch(shape->type){
        case CP_CIRCLE_SHAPE:
            drawCircleShape(shape, object);
            break;
        case CP_SEGMENT_SHAPE:
            drawSegmentShape(shape, object);
            break;
        case CP_POLY_SHAPE:
            drawPolyShape(shape, object);
            break;
        default:
            break;//printf("Bad enumeration in drawObject().\n");
    }
}

void sdGraphics::drawCollisions(void *ptr, void *data) {
    cpArbiter *arb = (cpArbiter *)ptr;
    for(int i=0; i<arb->numContacts; i++){
        cpVect v = arb->contacts[i].p;
        glVertex2f(v.x, v.y);
    }
}

void sdGraphics::display() {
    //glClear(GL_COLOR_BUFFER_BIT);
    glBegin(GL_QUADS);
    glColor4f(0.0, 0.0, 0.0, 0.1);
    glVertex2f(-WIDTH + (WIDTH / 2), -HEIGHT + (HEIGHT / 2));
    glVertex2f( WIDTH - (WIDTH / 2), -HEIGHT + (HEIGHT / 2));
    glVertex2f( WIDTH - (WIDTH / 2),  HEIGHT - (HEIGHT / 2));
    glVertex2f(-WIDTH + (WIDTH / 2),  HEIGHT - (HEIGHT / 2));
    glEnd();
    
	universe_->update();
	
	cpSpaceHashEach(universe_->getSpace()->activeShapes, &drawObject, &universe_->getObjects());

    glutSwapBuffers();
}

void sdGraphics::timercall(int value) {
    glutTimerFunc(SLEEP_TICKS, timercall, 0);	
    glutPostRedisplay();
}

void sdGraphics::initGL() {

    //todo get the width/height from elsewhere
    int width = WIDTH;
    int height = HEIGHT; 

    glClearColor(0.0, 0.0, 0.0, 0.0);

    glPointSize(3.0);

    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
    glHint(GL_POINT_SMOOTH_HINT, GL_DONT_CARE);
    glLineWidth(2.5f);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-(width / 2), (width / 2), -(height / 2), (height / 2), -1.0, 1.0);
    glTranslatef(0.5, 0.5, 0.0);    
}

void sdGraphics::init(sdUniverse* u, int width, int height, int argc, const char *argv[]) {
    universe_ = u;
    glutInit(&argc, (char**)argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowSize(width, height);
    glutCreateWindow("Progrezion V2");
    initGL();
    glutDisplayFunc(display);
    glutTimerFunc(SLEEP_TICKS, timercall, 0);

    //Input functions
    glutKeyboardFunc(universe_->getInputClass().keyboardCb);
    glutKeyboardUpFunc(universe_->getInputClass().keyboardUpCb);
    glutPassiveMotionFunc(universe_->getInputClass().mouseCb);
	glutMouseFunc(universe_->getInputClass().mouseClickCb);
    //	glutIdleFunc(idle);

}

